/*
Copyright (c) 2009, Young Digital Planet SA
All rights reserved.

* Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this 
  list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, 
  this list of conditions and the following disclaimer in the documentation 
  and/or other materials provided with the distribution.
* Neither the name of the Young Digital Planet SA nor the names of its 
  contributors may be used to endorse or promote products derived from this 
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
package pl.ydp.nexl.impl
{
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.system.ApplicationDomain;
	import flash.system.SecurityDomain;
	
	import pl.ydp.nexl.INexlAttributes;
	import pl.ydp.nexl.INexlExtensionDescription;
	import pl.ydp.nexl.INexlLibrary;
	import pl.ydp.nexl.INexlLibraryContainer;
	import pl.ydp.nexl.ReqLibraryLoadMonitor;
	import pl.ydp.nexl.impl.loader.ILibraryLoader;
	import pl.ydp.nexl.impl.loader.LocalLoader;
	import pl.ydp.nexl.impl.loader.SwfLoader;
	import pl.ydp.nexl.plugin.INexlPluginContainer;
	import pl.ydp.nexl.plugin.resource.ILibraryResource;
	import pl.ydp.nexl.utils.Libraries;
	
	/**
	 *  Dispatched when NexlLibrary definition is loaded
	 *  @eventType flash.events.Event.COMPLETE
	 */
	[Event(name="complete", type="flash.events.Event")]
	
	public class NexlLibrary extends EventDispatcher implements IExtensionsContainer, INexlPluginContainer
	{	
		protected var m_libraryUrl:String;
		protected var m_baseUrl:String;
		protected var m_libraryLoadType:String;
		
		internal var m_extensionsByType:Object;
		internal var m_resourcesById:Object;
		internal var m_extensions:Array;
		internal var m_parent:INexlLibraryContainer;
		internal var m_activatorClass:String;
		internal var m_id:String;
		internal var m_requiredLibs:Array;
		internal var m_loader:ILibraryLoader;
		internal var m_moduleAppDomain:ApplicationDomain;
		internal var m_moduleSecDomain:SecurityDomain;
		
		private var _monito:ILibraryLoadMonitor;
		
		/**
		 * It's enabled to use swf import?
		 */
		public var useSwfImport:Boolean;
		
		/**
		 * Create library
		 * @param url library swf file url
		 * @param burl library base url
		 * @param appDomain library app domain
		 * @param secDomain library security domain
		 */
		public function NexlLibrary( url:String, burl:String = null, appDomain:ApplicationDomain = null, secDomain:SecurityDomain = null ) {
			m_extensions = [];
			m_extensionsByType = { };			
			m_libraryUrl = url;
			if ( m_libraryUrl!=null && burl==null ) {
				var six:int = m_libraryUrl.lastIndexOf("/");
				m_baseUrl = m_libraryUrl.substring( 0, six+1 ); 
			} else
				m_baseUrl = burl;
			
			m_moduleAppDomain = appDomain==null ?
				new ApplicationDomain(ApplicationDomain.currentDomain) :
				appDomain;
			m_moduleSecDomain = secDomain;
			
			m_requiredLibs = new Array();
		}
		
	
		public function get id():String {
			return m_id==null ? m_libraryUrl.replace(/[:\/\\]/g,".") : m_id;
		}
		
		public function get parent():INexlLibraryContainer {
			return m_parent;
		}
		public function set parent(s:INexlLibraryContainer):void{
			m_parent = s;
		}

		public function get baseUrl():String {
			return m_baseUrl;
		}
		
		public function list( description:Object ):Array {
			var type:String = description.type;
			if ( type!=null && m_extensionsByType[type]) {
				var d2:Object = { };
				var hasFilter:Boolean = false;
				for( var n:String in description ) {
					if ( n!="type" ) {
						d2[n] = description[n];
						hasFilter = true;
					}
				}
				return hasFilter ? filter(m_extensionsByType[type],d2) : m_extensionsByType[type];
			} else
				return filter(m_extensions, description);
		}
		
		public function getLibraryResource( id:String ):ILibraryResource {
			return m_resourcesById[id];
		}
		
		public function get loaded():Boolean {
			return m_loader==null ? false : m_loader.loaded; 
		}
		
		public function getDefinition( name:String ):Object {
			if ( !loaded )
				throw new IllegalOperationError( "Library not loaded" );
			return m_moduleAppDomain.getDefinition( name );
		}

		public function loadData( library:XML ):void {
			if ( library.hasOwnProperty("@loadType") )
				m_libraryLoadType = library.@loadType;
			else
				m_libraryLoadType = ImplConstants.LIBRARY_LOAD_TYPE_SWF;
				
			if (m_libraryUrl==null && library.@src) {
				m_libraryUrl = (baseUrl)? baseUrl +"/"+ library.@src : library.@src;
			}
			
			if ( library.hasOwnProperty("@id") )
			{
				m_id = library.@id;
			}
			
			if(library.requires.hasOwnProperty("@library"))
			{
				for each (var req:XML in library.requires) {
					m_requiredLibs.push(req.@library.toString());
				}
			}
			
			var activators:XMLList = library.activator; 
			if ( activators.length()>0 )
				m_activatorClass = selectActivator( activators ); 
			
			for each( var ext:XML in library.ext ) {
				
				var next:NexlExtension = createExtension( ext );
			 	
			 	next.loadData( ext );
			 	var type:String = next.type;
			 	var v:Array = m_extensionsByType[type];
			 	if ( v==null )
			 		m_extensionsByType[type] = [next];
			 	else
			 		v.push(next);
			 	
			 	m_extensions.push(next);
			}
			
			m_resourcesById = { };
			for each( var resx:XML in library.resource ) {
				var res:LibraryResource = createResource( resx );
				if ( res!=null ) {
					res.loadData( resx );
					m_resourcesById[res.id] = res;
				}
			}
			
			dispatchEvent( new Event( Event.COMPLETE ) );
		}
		
		protected function createResource( node:XML ):LibraryResource {
			if ( node.@type=="swf" )
				return new SwfLibraryResource(this);
			else
				return new LibraryResource(this);
		}
		
		protected function isSingleton( ext:XML ):Boolean {
			return (ext.@singleton=="true");
		}
		
		protected function createExtension( ext:XML ):NexlExtension {
			if (isSingleton(ext)) {
				return new NexlSingleton( this );
			}
			return new NexlExtension( this );
		}
		
		public function get activatorClass():String {
			return m_activatorClass;
		}
		
		protected function selectRequiredLib( reqmnts: XMLList) :String {
			return reqmnts[0].attribute("library");
		}
		
		protected function selectActivator( activators:XMLList ):String {
			return activators[0].attribute( "class" );
		}
		
		protected function get extensionsByType():Object {
			return m_extensionsByType;
		}
		protected function get extensions():Array {
			return m_extensions;
		}
		
		public function loadLibrary( monito:ILibraryLoadMonitor ):void {
			if (m_requiredLibs.length>0 && m_loader==null) {
				//
				if ( m_libraryLoadType==ImplConstants.LIBRARY_LOAD_TYPE_SWF )
					m_loader = new SwfLoader( this );
				else if ( m_libraryLoadType==ImplConstants.LIBRARY_LOAD_TYPE_LOCAL )
					m_loader = new LocalLoader( this );
				//
				m_loader.addMonitor(monito);
				var reqLibMonitor:ReqLibraryLoadMonitor = new ReqLibraryLoadMonitor(loadSWFFile, reqLibLoadError);
				var sParent:INexlLibraryContainer = Libraries.findSuperParent(this);
				//
				for each (var reqid:String in m_requiredLibs) {
					var foundLib:IExtensionsContainer = sParent.findLibraryById(reqid) as IExtensionsContainer;
					if (foundLib!=null)	{
						reqLibMonitor.incLibsNum();
						foundLib.loadLibrary(reqLibMonitor);
					} else {
						monito.failed(this, "Required precedent library: "+reqid+" not found.");
						break;
					}
				}
			} else {
				if (m_loader==null)	{
					if ( m_libraryLoadType==ImplConstants.LIBRARY_LOAD_TYPE_SWF )
						m_loader = new SwfLoader( this );
					else if ( m_libraryLoadType==ImplConstants.LIBRARY_LOAD_TYPE_LOCAL )
						m_loader = new LocalLoader( this );
					//	
				}
				m_loader.load( monito );
			}
		}
		public function loadSWFFile():void{
			m_loader.load( m_loader.getMonitor() );
		}
		
		public function reqLibLoadError(ext:INexlLibrary, cause:String):void {
			m_loader.getMonitor().failed(this, cause);
		}
		
		public function get libraryUrl():String {
			return m_libraryUrl;
		}
		
		public function get libraryApplicationDomain():ApplicationDomain {
			return m_moduleAppDomain;
		}
		
		public function get librarySecurityDomain():SecurityDomain {
			return m_moduleSecDomain;
		}
		
		static protected function filter( exts:Array, filter:Object ):Array {
			return exts.filter( function(item:INexlExtensionDescription, index:int, array:Array):Boolean {
				for( var n:String in filter ) {
					var atts:INexlAttributes = item.attributes;
					if ( !atts.attributeHasValue(n,filter[n]) )
						return false;
				}
				return true;
			} );
		}
	}
}